//===- AMDMnemonicMapper.cpp - Generate opcode to mnemonic code -----------===//
//
//
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend generates code to get instruction mnemonic by opcode.
//
//===----------------------------------------------------------------------===//

#include "AMDMnemonicMapperEmitter.h"
#include "CodeGenTarget.h"
#include "llvm/ADT/Twine.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/StringToOffsetTable.h"
#include <sstream>

using namespace llvm;

/// EmitGetInstMnemonic - Generate the code for the "GetInstMnemonic" method
/// implementation.
void MnemonicMapperEmitter::EmitGetInstMnemonic(raw_ostream &O) {
  CodeGenTarget Target(Records);

  O << "/// getInstMnemonic - This method is automatically generated by tblgen\n"
       "/// from the instruction set description. This returns the assembler mnemonic\n"
       "/// for the specified machine instruction opcode.\n";
  O << "static const char* getInstMnemonic(const MachineInstr *MI) {\n";
  O << "  static const unsigned OpInfo[] = {\n";

  const std::vector<const CodeGenInstruction *> &Instructions =
    Target.getInstructionsByEnumValue();
  StringToOffsetTable StringTable;

  unsigned predef = 0, e = Instructions.size();
  for ( ; predef != e; ++predef) {
    if (Instructions[predef]->Namespace != "TargetOpcode")
      break;
  }
  for (unsigned i = predef; i != e; ++i) {
    StringRef Asm = Instructions[i]->AsmString;
    size_t len = Asm.find_first_of(" \t\n\r");
    if (len != StringRef::npos)
      Asm = Asm.slice(0, len);
    O << "    " << StringTable.GetOrAddStringOffset(Asm) << "U";
    if (i != (e - 1))
      O << ",";
    O << " // " << Instructions[i]->TheDef->getName() << "\n";
  }
  O << "  };\n\n";

  O << "  static const char AsmStrs[] = {\n";
  StringTable.EmitString(O);
  O << "\n  };\n\n";

  O << "  unsigned InstIndex = MI->getOpcode() - "
    << Instructions[predef]->Namespace << "::"
    << Instructions[predef]->TheDef->getName() << ";\n";
  O << "  assert(InstIndex < sizeof(OpInfo)/sizeof(OpInfo[0]));\n";
  O << "  return &AsmStrs[OpInfo[InstIndex]];\n";
  O << "}\n";
}

static void emitRegisterNameString(raw_ostream &O, StringRef AltName,
              const std::vector<CodeGenRegister*> &Registers) {
  StringToOffsetTable StringTable;
  O << "  static const unsigned RegAsmOffset" << AltName << "[] = {\n    ";
  for (unsigned i = 0, e = Registers.size(); i != e; ++i) {
    const CodeGenRegister &Reg = *Registers[i];

    std::string AsmName;
    // "NoRegAltName" is special. We don't need to do a lookup for that,
    // as it's just a reference to the default register name.
    if (AltName == "" || AltName == "NoRegAltName") {
      AsmName = Reg.TheDef->getValueAsString("AsmName");
      if (AsmName.empty())
        AsmName = Reg.getName();
    } else {
      // Make sure the register has an alternate name for this index.
      std::vector<Record*> AltNameList =
        Reg.TheDef->getValueAsListOfDefs("RegAltNameIndices");
      unsigned Idx = 0, e;
      for (e = AltNameList.size();
           Idx < e && (AltNameList[Idx]->getName() != AltName);
           ++Idx)
        ;
      // If the register has an alternate name for this index, use it.
      // Otherwise, leave it empty as an error flag.
      if (Idx < e) {
        std::vector<std::string> AltNames =
          Reg.TheDef->getValueAsListOfStrings("AltNames");
        if (AltNames.size() <= Idx)
          PrintFatalError(Reg.TheDef->getLoc(),
                        (Twine("Register definition missing alt name for '") +
                        AltName + "'.").str());
        AsmName = AltNames[Idx];
      }
    }

    O << StringTable.GetOrAddStringOffset(AsmName);
    if (((i + 1) % 14) == 0)
      O << ",\n    ";
    else
      O << ", ";

  }
  O << "0\n"
    << "  };\n"
    << "\n";

  O << "  static const char *AsmStrs" << AltName << " =\n";
  StringTable.EmitString(O);
  O << ";\n";
}

void MnemonicMapperEmitter::EmitGetRegisterName(raw_ostream &O) {
  CodeGenTarget Target(Records);
  const std::vector<CodeGenRegister*> &Registers =
    Target.getRegBank().getRegisters();
  std::vector<Record*> AltNameIndices = Target.getRegAltNameIndices();
  bool hasAltNames = AltNameIndices.size() > 1;

  O << "\n"
  "/// getRegisterName - This method is automatically generated by tblgen\n"
  "/// from the register set description.  This returns the assembler name\n"
  "/// for the specified register.\n"
  "static const char *";
  if (hasAltNames)
    O << "\ngetRegisterName(unsigned RegNo, unsigned AltIdx) {\n";
  else
    O << "getRegisterName(unsigned RegNo) {\n";
  O << "  assert(RegNo && RegNo < " << (Registers.size()+1)
    << " && \"Invalid register number!\");\n"
    << "\n";

  if (hasAltNames) {
    for (unsigned i = 0, e = AltNameIndices.size(); i < e; ++i)
      emitRegisterNameString(O, AltNameIndices[i]->getName(), Registers);
  } else
    emitRegisterNameString(O, "", Registers);

  if (hasAltNames) {
    O << "  const unsigned *RegAsmOffset;\n"
      << "  const char *AsmStrs;\n"
      << "  switch(AltIdx) {\n"
      << "  default: assert(0 && \"Invalid register alt name index!\");\n";
    for (unsigned i = 0, e = AltNameIndices.size(); i < e; ++i) {
      StringRef Namespace = AltNameIndices[1]->getValueAsString("Namespace");
      StringRef AltName(AltNameIndices[i]->getName());
      O << "  case " << Namespace << "::" << AltName
        << ":\n"
        << "    AsmStrs = AsmStrs" << AltName  << ";\n"
        << "    RegAsmOffset = RegAsmOffset" << AltName << ";\n"
        << "    break;\n";
    }
    O << "}\n\n";
  }

  O << "\n"
    << "  assert (AsmStrs[RegAsmOffset[RegNo-1]] &&\n"
    << "          \"Invalid alt name index for register!\");\n"
    << "  return &AsmStrs[RegAsmOffset[RegNo-1]];\n"
    << "}\n";
}

void MnemonicMapperEmitter::run(raw_ostream &O) {
  EmitGetInstMnemonic(O);
  EmitGetRegisterName(O);
}

namespace llvm {

void EmitMnemonicMapper(RecordKeeper &RK, raw_ostream &OS) {
  emitSourceFileHeader("MnemonicMapper class implementation", OS);
  MnemonicMapperEmitter(RK).run(OS);
}

} // End llvm namespace
